using System;
using System.Collections;

namespace Framework
{
	// An enumerator over shell menu items; filters and reorders the items
	// based on the context menu options
	internal class MenuOrderEnumerator : IEnumerator
	{
		private readonly bool _defaultOnly;
		private readonly bool _explore;
		private int _i = -1;
		private readonly ShellMenuItem[] _menuItems;

		internal MenuOrderEnumerator(ShellMenuItem[] menuItems, ContextMenuOptions opts)
		{
			if (menuItems == null)
			{
				throw new ArgumentNullException("menuItems");
			}

			this._menuItems = menuItems;
			this._defaultOnly = (opts & ContextMenuOptions.DefaultOnly) == ContextMenuOptions.DefaultOnly;
			this._explore = (opts & ContextMenuOptions.Explore) == ContextMenuOptions.Explore;
		}

		#region IEnumerator Members

		public void Reset()
		{
			this._i = -1;
		}

		public object Current
		{
			get
			{
				// if the explore and open items should be swapped because the
				// explorer tree view is showing
				if (!this._defaultOnly &&
					HasExploreItem && HasOpenItem &&
					((this._explore && !ExploreBeforeOpen) ||
					 (!this._explore && ExploreBeforeOpen)))
				{
					// swap them

					if (this._i == ExploreItemIndex)
					{
						return this._menuItems[OpenItemIndex];
					}
					if (this._i == this.OpenItemIndex)
					{
						return this._menuItems[this.ExploreItemIndex];
					}
				}

				return this._menuItems[this._i];
			}
		}

		public bool MoveNext()
		{
			while (++this._i < this._menuItems.Length)
			{
				if (Included(this._menuItems[this._i]))
				{
					break;
				}
			}

			return this._i < this._menuItems.Length;
		}

		#endregion

		// Gets the ID of the current menu item
		internal int CurrentId
		{
			get
			{
				for (int i = 0; i < this._menuItems.Length; i++)
				{
					if (this._menuItems[i] == Current)
					{
						return i;
					}
				}

				return -1;
			}
		}

		// Gets whether the menu item in Current should be the default menu
		// item; this overrides the Current.Default property because the
		// default menu item can change depending on whether the 'Explore'
		// option was set by the shell
		internal bool CurrentIsDefault
		{
			get { return ItemIsDefault((ShellMenuItem)Current); }
		}

		// Gets whether the Explore menu item precedes the Open menu item
		// in the natural order of the menu items
		private bool ExploreBeforeOpen
		{
			get { return ExploreItemIndex < OpenItemIndex; }
		}

		// The index of the Explore menu item, or -1 if there is no explore
		// menu item
		private int ExploreItemIndex
		{
			get
			{
				for (int i = 0; i < this._menuItems.Length; i++)
				{
					if (IsExploreItem(this._menuItems[i]))
					{
						return i;
					}
				}

				return -1;
			}
		}

		// Gets whether there is an explore menu item in the enumeration
		private bool HasExploreItem
		{
			get { return ExploreItemIndex != -1; }
		}

		// Gets whether there is an open menu item in the enumeration
		private bool HasOpenItem
		{
			get { return OpenItemIndex != -1; }
		}

		// Gets whether the item at the specified index is the default
		// item; the default item can change between the 'Explore' and 'Open'
		// menu items depending on whether the shell set the Explore context
		// menu option
		private bool ItemIsDefault(ShellMenuItem item)
		{
			return !this._explore && IsOpenItem(item) ||
				   (this._explore && IsExploreItem(item) || ((!this._explore && !this.HasOpenItem) ||
															 (this._explore && !this.HasExploreItem)) && item.Default);
		}

		// Gets whether the menu item at the specified index is included
		// in the context menu
		private bool Included(ShellMenuItem item)
		{
			if (this._defaultOnly)
			{
				return ItemIsDefault(item);
			}

			return true;
		}

		// Gets whether the specified menu item is the 'explore' menu item
		private static bool IsExploreItem(ShellMenuItem item)
		{
			return item.Verb == ExploreMenuItem.ExploreVerb;
		}

		// Gets whether the specified menu item is the 'open' menu item
		private static bool IsOpenItem(ShellMenuItem item)
		{
			return item.Verb == ExploreMenuItem.OpenVerb;
		}

		// The index of the open menu item, or -1 if there is no open menu
		// item
		private int OpenItemIndex
		{
			get
			{
				for (int i = 0; i < this._menuItems.Length; i++)
				{
					if (IsOpenItem(this._menuItems[i]))
					{
						return i;
					}
				}

				return -1;
			}
		}
	} ;
}
